/************************************************************************
Copyright (c) 2010, 2013, C.R. All rights reserved. 

E-mail: crtoyou@gmail.com; crtoyou@163.com

You can redistribute it and/or modify it under the terms of the
GNU Lesser General Public License as published by the
Free Software Foundation version 3 of the License.

This source code is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program. if not, write to the Free Software Foundation, Inc., 
51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
************************************************************************/

#ifndef __CR_ENCRYPT_H_20120114__
#define __CR_ENCRYPT_H_20120114__

#include <tchar.h>
#include <crdebug.h>
#include <crdefine.h>
#include <import/md5.h>

namespace cr {

inline uint8 chksum(const byte* buf, size_t len)
{
	uint8 chk = 0;
	for (size_t i=0; i<len; ++i)
		chk ^= buf[i];
	return chk;
}
inline uint8 chksum_ror(const byte* buf, size_t len)
{
	uint8 chk = 0;
	for (size_t i=0; i<len; ++i) {
		chk ^= buf[i];
		chk = (chk >> 1) | (chk << 7);
	}
	return chk;
}
inline void hashkey(uint128& key, const byte* buf, size_t len)
{
	MD5Context md5;
	MD5Init(&md5);
	MD5Update(&md5, buf, len);
	MD5Final(key.by, &md5);
}

class hash_md5
{
public:
	hash_md5() : val(0) {name_[0] = NULL;}

	inline LPCTSTR name() const {return name_;}

	inline void begin(TCHAR* text = _T(""))
	{
		_tcscpy_s(name_, 64, text);
		MD5Init(&ctx);
	}
	inline void update(const byte* buf, size_t len)
	{
		MD5Update(&ctx, buf, (unsigned)len);
	}
	inline uint128 final()
	{
		MD5Final(val.by, &ctx);
		return val;
	}
	template <size_t _Size>
	inline void hexprint(TCHAR (&_Dst)[_Size])
	{
		_stprintf_s<_Size>(_Dst, 
			_T("%02X%02X%02X%02X")
			_T("%02X%02X%02X%02X")
			_T("%02X%02X%02X%02X")
			_T("%02X%02X%02X%02X"),
			val.by[0], val.by[1], val.by[2], val.by[3],
			val.by[4], val.by[5], val.by[6], val.by[7],
			val.by[8], val.by[9], val.by[10], val.by[11],
			val.by[12], val.by[13], val.by[14], val.by[15]);
	}

protected:
	TCHAR name_[64];
	MD5Context ctx;
	uint128 val;
};

class encrypt
{
public:
	inline void setkey(uint128 key) {key_ = key;}
protected:
	encrypt(uint128 key = 0):key_(key) {}
	uint128 key_;
};

class encrypt1 : public encrypt
{
public:
	encrypt1(uint128 key = 0) : encrypt(key) {}

	// the same flow of encoding and decoding
	__forceinline void __fastcall operator()(byte* inout, size_t len, uint16& state)
	{
		const byte* end = inout + len;
		byte c = state & 0xFF;
		byte r = state >> 8;
		for (byte* p=inout; p<end; ++p)
		{
			byte k = key_.by[r++&7];
			c ^= k + r;
			c += 97; //97 is prime
			c = (c >> 3) | (c << 5);
			*p ^= c ^ k;
		}
		state = (r << 8) | c;
	}
};

}//namespace

#endif //__CR_ENCRYPT_H_20120114__